/*--Autor - Pawel M. lopusinski, grupa 1I3--*/
/*--Zadanie - Program liczacy calke z funkcji a*sinx + b*cosx metoda Monte-Carlo na zadanym przedziale i z zadana iloscia probkowania--*/

/*--biblioteki zewnetrzne--*/
/*--stdio.h -> scanf, printf--*/
/*--math.h -> funkcje matematyczne--*/
/*--stdlib.h -> rand(), atof()--*/
/*--string.h -> strstr()--*/
/*--time.h -> time(0)--*/
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

/*--definicje stalych--*/
/*--mEPS > stala wykorzystywana w przeszukiwaniu funkcji, pozostale > stale matemtyczne wykorzystywane w parserze--*/
# define mEPS		0.000001
# define M_E		2.7182818284590452354
# define M_LOG2E	1.4426950408889634074
# define M_LOG10E	0.43429448190325182765
# define M_LN2		0.69314718055994530942
# define M_LN10		2.30258509299404568402
# define M_PI		3.14159265358979323846

/*--makra funkcji zwracajacych wieksza(mniejsza) z dwoch wartosci--*/
# define mMax(a, b) (a > b ? a : b)
# define mMin(a, b) (a < b ? a : b)

/*--zmienne globalne--*/
long double a = 0, b = 0;
long ile = 0, obroty = 0;
int strona = 1, kazda = 0;

/*--Zadana funkcja--*/
long double f(long double x) { return a*sin(x) + b*cos(x); }

/*--prototypy funkcji--*/
long double lookForMinMax(int znak, long double lewo, long double prawo);

long double calkuj(long double lewo, long double prawo);

long double parse(char *string);

int rnd(int min, int max);

/*--Glowna funkcja programu--*/
int main(int argc, char* argv[]) {
	char *input = malloc(100);
	int i = 1;
	long double calka, granicaL = 0, granicaP = 0, seed = 0;	
	
	/*--przywitanie i krotki opis dzialania programu--*/
	printf("Program liczy calke metoda Monte-Carlo z funkcji a*sin(x) + b*cos(x)\n\n");
	printf("Podajac wartosci dla parametrow a i b oraz krancow przedzialu calkowania mozna wpisac wartosci \"literowo\".\n");
	printf("Akceptowane wartosci \"literowe\" i ich wartosci liczbowe : \n");
	printf("pi : %f | e : %f | log2e : %f | log10e : %f\nln2 : %f | ln10 : %f\n", M_PI, M_E, M_LOG2E, M_LOG10E, M_LN2, M_LN10);
	printf("WAzNE! Wszelkie wartosci liczbowe podane PO literowych zostana opuszczone przez program!\n");
	printf("Przyklad 1 : 10pie - zapis poprawny -> zostanie zinterpretowany jako 10*pi*e\n");
	printf("Przyklad 2 : pie10 - zapis niepoprawny, zostanie zinterpretowany jako pi*e\n\n");
	printf("WAzNE! Wartosci ulamkowe podajemy z kropka! Przyklad : 3.14 OK | 3,14 zLE\n\n");
	printf("Uwaga - program dziala metoda losowa, wynik moze odbiegac od faktycznego z tolerancja zalezna od ilosci losowanych punktow.\n\n");
    
    /*--pobieranie danych potrzebnych do dzialania programu, ew. wpisywanie ich z parametrow wywolania--*/
    if ((argv[i] != NULL) && (i <= argc)) {
		input = argv[i];
		i++;
		printf("Wartosc parametru 'a' zostala pobrana z parametrow wywolania\n");
	} else {
		printf("Prosze podac parametr 'a' : ");
		scanf("%s", input);
	}
	a = parse(input);
	
	if ((argv[i] != NULL) && (i <= argc)) {
		input = argv[i];
		i++;
		printf("Wartosc parametru 'b' zostala pobrana z parametrow wywolania\n");
	} else {
		printf("Prosze podac parametr 'b' : ");
		scanf("%s", input);
	}
	b = parse(input);
	
	if ((argv[i] != NULL) && (i <= argc)) {
		input = argv[i];
		i++;
		printf("Wartosc lewej granicy zostala pobrana z parametrow wywolania\n");
	} else {
		printf("Prosze podac lewa granice calkowania' : ");
		scanf("%s", input);
	}
	granicaL = parse(input);
	
	if ((argv[i] != NULL) && (i <= argc)) {
		input = argv[i];
		i++;
		printf("Wartosc prawej granicy zostala pobrana z parametrow wywolania.\n");
	} else {
		printf("Prosze podac prawa granice calkowania : ");
		scanf("%s", input);
	}
	granicaP = parse(input);
	if (granicaP < granicaL) {
		strona = -1;
	}
	
	if ((argv[i] != NULL) && (i <= argc)) {
		input = argv[i];
		i++;
		printf("Ilosc punktow do wylosowania zostala pobrana z parametrow wywolania.\n");
	} else {
		printf("Prosze podac ilosc punktow do wylosowania : ");
		scanf("%s", input);
	}
	ile = (int)fabs(parse(input));
	
	if ((argv[i] != NULL) && (i <= argc)) {
		input = argv[i];
		i++;
		printf("Ilosc przejsc zostala pobrana z parametrow wywolania.\n");
	} else {
		printf("\nMozna zdefiniowac ilosc przejsc funkcji - wynik koncowy jest srednia arytmetyczna uzyskanych wynikow.\n");
		printf("Prosze podac ilosc przejsc : ");
		scanf("%s", input);
	}
	obroty = (int)fabs(parse(input));
		
	if (obroty > 1) {
		if ((argv[i] != NULL) && (i <= argc)) {
			input = argv[i];
			i++;
			printf("Decyzja o wyswietlaniu zostala pobrana z parametrow wywolania.\n");
		} else {
			printf ("Czy chcesz wyswietlic wartosci dla kazdego przejscia? [T/N] : ");
			scanf("%s", input);
		}
		if ( (input[0] == *"t") || (input[0] == *"T") || (input[0] == *"1" ) ) kazda = 1;
	}
	
	if ((argv[i] != NULL) && (i <= argc)) {
		input = argv[i];
		i++;
		printf("Wartosc ziarna zostala pobrana z parametrow wywolania.\n");
	} else {
		printf("Mozna podac wlasna wartosc ziarna dla randomizera (np. dla pewnosci otrzymania dokladnie tego samego wyniku).\n");
		printf("Aby wykorzystac wartosc pobrana z zegara systemowego(unikatowa) wpisz '0' : ");
		scanf("%s", input);
	}
	seed = parse(input);
	seed == 0 ? srand(time(0)) : srand(seed);
	
	/*--obliczanie wartosci calki--*/
	printf("Prosze czekac, trwa calkowanie.\nDlugosc trwania procesu uzalezniona jest od wartosci podanych parametrow oraz mocy obliczeniowej komputera.\n\n");
	calka = strona * calkuj(granicaL, granicaP);	
	printf("Wartosc calki dla podanych parametrow to : %Lf \n", calka);
	
	return 0;
}

/*--funkcja parsujaca dane z wejscia i zwracajaca ich wartosc liczbowa--*/
long double parse(char* string) {
	
	long double result = 1;
	
	if ( (strstr(string, "pi") != NULL) || (strstr(string, "PI") != NULL) ) result *= M_PI;
	if ( (strstr(string, "e") != NULL) || (strstr(string, "E") != NULL) ) result *= M_E;
	if ( (strstr(string, "log2e") != NULL) || (strstr(string, "LOG2E") != NULL) ) result *= M_LOG2E;
	if ( (strstr(string, "log10e") != NULL) || (strstr(string, "LOG10E") != NULL) ) result *= M_LOG10E;
	if ( (strstr(string, "ln2") != NULL) || (strstr(string, "LN2") != NULL) ) result *= M_LN2;
	if ( (strstr(string, "ln10") != NULL) || (strstr(string, "LN10") != NULL) ) result *= M_LN10;
	if ( strstr(string, "-") != NULL ) result *= -1;
	if ( !((atof(string) == 0) && (string[0] != *"0")) ) result *= (atof(string) < 0 ? (-1)*atof(string) : atof(string));
	
	return result;
}


/*--szukanie wartosci maksymalnej funkcji - z wlasnosci sin i cos wystarczy przeszukac przedzial maksymalnie <0,2*PI>--*/
long double lookForMinMax(int znak, long double lewo, long double prawo) {
	
	long double result = 0;
	
	switch(znak) {
		case 1 : {
			if(fabs(lewo - prawo) < 2*M_PI) {
				long double x = mMin(lewo, prawo);
				while (x <= mMax(lewo, prawo)) {
					if (f(x) > result) result = f(x);
					x += mEPS;
				}
			} else {
				long double x = -2*M_PI;
				while (x <= 2*M_PI) {
					if (f(x) > result) result = f(x);
					x += mEPS;
				}
			}
			if (result < 0) result = 0;
		} break;
		case -1 : {
			if(fabs(lewo - prawo) < 2*M_PI) {
				long double x = mMin(lewo, prawo);
				while (x <= mMax(lewo, prawo)) {
					if (f(x) < result) result = f(x);
					x += mEPS;
				}
			} else {
				long double x = -2*M_PI;
				while (x <= 2*M_PI) {
					if (f(x) < result) result = f(x);
					x += mEPS;
				}
			}
			if (result > 0) result = 0;
		} break;
		default : break;
	}
	
	return result;
}

/*--funkcja calkujaca--*/
long double calkuj(long double lewo, long double prawo) {
	

	long double wynik = 0, pole, x, y, max, min;
	long podp, podm, i, j;
	
	max = lookForMinMax(1, lewo, prawo);
	min = lookForMinMax(-1, lewo, prawo);
	pole = fabs(prawo - lewo) * fabs(max - min);
	max *= 100;
	min *= 100;
	lewo *= 100;
	prawo *= 100;
	
	for (j = 0; j < obroty; j++) {
		podm = podp = 0;
		for (i = 0; i < ile; i++) {
			x = ( (long double)(rnd((int)lewo, (int)prawo +1)) ) / 100; 
			y = ( (long double)(rnd((int)min, (int)max +1)) ) / 100;
			if (x >= 0) {
				if ( (y <= 0) && (f(x) < 0) && (y > f(x)) ) podm++;
				if ( (y >= 0) && (f(x) > 0) && (y < f(x)) ) podp++;
			} else {
				if ( (y <= 0) && (f(x) < 0) && (y > f(x)) ) podm++; 
				if ( (y >= 0) && (f(x) > 0) && (y < f(x)) ) podp++;
			}
		}
		if (kazda == 1) printf("Calka %ld : %Lf\n", j, ((pole*((long double)podp/(long double)ile)) - (pole*((long double)podm/(long double)ile))));
		wynik += ((pole*((long double)podp/(long double)ile)) - (pole*((long double)podm/(long double)ile)));
	}
	return wynik/obroty;
}

int rnd(int min, int max) {
  if (max>=min) {
    max -= min;
  } else {
    int tmp = min - max;
    min = max;
    max = tmp;
  }
  return max ? rand() % max + min : min;
}
